package workerpool1

import (
	"errors"
	"fmt"
	"sync"
)

type Task func() // 定义任务的类型

type Pool struct {
	capacity int           // 容量
	active   chan struct{} // 可调用
	tasks    chan Task     // 需要完成的任务

	wg   sync.WaitGroup
	quit chan struct{} // 离开标识
}

const (
	defaultCapacity = 100
	maxCapacity     = 1000
)

func New(capacity int) *Pool {
	if capacity <= 0 {
		capacity = defaultCapacity

	}
	if capacity > maxCapacity {
		capacity = maxCapacity
	}
	p := &Pool{
		capacity: capacity,
		tasks:    make(chan Task),
		quit:     make(chan struct{}),
		active:   make(chan struct{}, capacity), // 带缓冲的 channel，表示 正在工作的worker

	}
	fmt.Printf("workerpool start\n")
	// 开启 workpool
	go p.run()
	return p
}

func (p *Pool) run() {
	// 表示 worker 编号
	idx := 0
	for {
		select {
		// 如果不堵塞，表示 workpool已经停止运转
		case <-p.quit:
			return
		// 如果没被堵塞，表示 worker有空闲，可以接收任务
		case p.active <- struct{}{}:
			idx++
			p.newWorker(idx)
		}
	}
}

func (p *Pool) newWorker(i int) {
	//
	p.wg.Add(1)
	// 开启一个 goroutine
	go func() {
		defer func() {
			if err := recover(); err != nil {
				fmt.Printf("worker[%03d]: recover panic[%s] and exit\n", i, err)
				//腾出一个位置
				<-p.active

			}
			// 完成一个任务
			p.wg.Done()
		}()
		fmt.Printf("worker[%03d]: start\n", i)
		for {
			select {
			// 任务开始前，检查是否到下班时间？
			case <-p.quit:
				fmt.Printf("worker[%03d]: exit\n", i)
				<-p.active
				return

			// 只能保证有一个任务，但是一旦有空闲的worker，便会立刻把这个任务取出
			case t := <-p.tasks:
				fmt.Printf("worker[%03d]: receive a task\n", i)
				t()
			}

		}
	}()
}

var ErrWorkerPoolFreed = errors.New("workerpool freed")

func (p *Pool) Schedule(t Task) error {
	select {
	case <-p.quit:
		return ErrWorkerPoolFreed
	case p.tasks <- t:
		return nil
	}
}

func (p *Pool) Free() {
	close(p.quit)
	p.wg.Wait()
	fmt.Printf("workerpool freed\n")
}
